#include "net.h"
#include <QDebug>
#include <QDataStream>

#define DEBUG			0

net::net(QObject *parent) :
	QObject(parent)
{
	ready = false;
	connect(&server, SIGNAL(newConnection()), this, SLOT(slot_client_connected()));
}

void net::start_server(quint16 port)
{
	ready = server.listen(QHostAddress::Any, port);
	qDebug() << "net::start_server ready=" << ready;
}

bool net::process_packet(QString client, QByteArray data)
{
	int offset = 0;
	log_event le;
	le.current_time = QDateTime::currentDateTime();
	le.str_current_time = le.current_time.toString();
	le.client_name = client;

	if(DEBUG) qDebug() << "-----" << endl << "processing packet" << data.size() << "bytes";

	le.msg_version = (quint8) data[offset++];
	if(DEBUG) qDebug() << "msg_version=" << le.msg_version;
	if(le.msg_version != 3) return false;
	le.unicode = (quint8) data[offset++];
	if(DEBUG) qDebug() << "unicode=" << le.unicode;

	le.server_name = get_string(data, offset);
	if(DEBUG) qDebug() << "server_name=" << le.server_name;
	le.logger_name = get_string(data, offset);
	if(DEBUG) qDebug() << "logger_name=" << le.logger_name;

	le.log_level = get_int(data, offset);
	if(DEBUG) qDebug() << "log_level=" << le.log_level;

	le.NDC = get_string(data, offset);
	if(DEBUG) qDebug() << "NDC=" << le.NDC;
	le.message = get_string(data, offset);
	if(DEBUG) qDebug() << "message=" << le.message;
	le.thread = get_string(data, offset);
	if(DEBUG) qDebug() << "thread=" << le.thread;

	le.sec = get_int(data, offset);
	le.usec = get_int(data, offset);
	if(DEBUG) qDebug() << "time=" << le.sec << ":" << le.usec;

	le.file = get_string(data, offset);
	if(DEBUG) qDebug() << "file=" << le.file;
	le.line = get_int(data, offset);
	if(DEBUG) qDebug() << "line=" << le.line;
	le.func = get_string(data, offset);
	if(DEBUG) qDebug() << "func=" << le.func;

	if(DEBUG) qDebug() << "-----" << endl;

	le.valid = true;

	emit packet_full(le);
	return true;
}

int net::get_int(QByteArray data, int &offset)
{
	int integer = ba2int(data, offset);
	offset += 4;
	return integer;
}

QString net::get_string(QByteArray data, int &offset)
{
	int sz_str = ba2int(data, offset);
	offset += 4;
	QString str = QString(data.mid(offset, sz_str));
	offset += sz_str;
	return str;
}

void net::slot_ready_read()
{
	QTcpSocket *s = (QTcpSocket*)sender();
	net_client *c = get_client(s);
	if(c == NULL) return;

	static int ready_reads = 0;//!!!
	ready_reads++;
	if(DEBUG) qDebug() << "ready_reads=" << ready_reads << "avail=" << c->socket->bytesAvailable();

	while(c->socket->bytesAvailable() >= 4)
	{
		if(c->rem)
		{
			QByteArray t = c->socket->read(c->rem);
			if(DEBUG) qDebug() << "data t=" << t.toHex();
			c->data.append(t);
			c->rem -= t.size();

			if(!c->rem) // full packet received, process
			{
				if(DEBUG) qDebug() << "PACKET size=" << c->data.size() << "from" << c->name;
				process_packet(c->name, c->data);
			}

			c->data.clear();
		}

		if(!c->rem && (c->socket->bytesAvailable() >= 4))
		{
			QByteArray t = c->socket->read(4);
			c->rem = ba2int(t, 0);
		}
	}
}

net_client* net::get_client(QTcpSocket *s)
{
	foreach(net_client* c, clients)
	{
		if(c->socket == s) return c;
	}
	return NULL;
}

void net::slot_client_connected()
{
	while(server.hasPendingConnections())
	{
		QTcpSocket *c = server.nextPendingConnection();
		if(!c) continue;

		net_client *cli = new net_client;
		cli->socket = c;
		cli->name = c->peerAddress().toString() + ":" + QString::number(c->peerPort());
		connect(cli->socket, SIGNAL(readyRead()), this, SLOT(slot_ready_read()));
		clients.append(cli);
		emit new_client(cli->name);
	}
}

QByteArray net::int2ba(quint32 value)
{
	QByteArray result;
	result.append((value & (0xFF<<24))>>24);
	result.append((value & (0xFF<<16))>>16);
	result.append((value & (0xFF<<8))>>8);
	result.append(value & 0xFF);
	return result;
}

quint32 net::ba2int(QByteArray ba, int offset)
{
	quint32 result = 0;
	if(ba.size() < (offset + 4)) return 0;
	result |= (ba[offset] & 0xFF) << 24;
	result |= (ba[offset + 1] & 0xFF) << 16;
	result |= (ba[offset + 2] & 0xFF) << 8;
	result |= (ba[offset + 3] & 0xFF);
	return result & 0xFFFFFFFF;
}

QByteArray log_event::vl2ba(QVariantList vl)
{
	QByteArray ba;
	QDataStream *stream = new QDataStream(&ba, QIODevice::WriteOnly);
	(*stream) << vl;
	delete stream;
	return ba;
}

QVariantList log_event::ba2vl(QByteArray ba)
{
	QVariantList vl;
	QDataStream *stream = new QDataStream(&ba, QIODevice::ReadOnly);
	(*stream) >> vl;
	delete stream;
	return vl;
}

QByteArray log_event::to_ba()
{
	QByteArray ba;
	if(!valid) return ba;

	QVariantList vl;
	vl.append(current_time);
	vl.append(client_name.toUtf8());
	vl.append((int)msg_version);
	vl.append((int)unicode);

	vl.append(server_name.toUtf8());
	vl.append(logger_name.toUtf8());
	vl.append(log_level);
	vl.append(NDC.toUtf8());
	vl.append(message.toUtf8());
	vl.append(thread.toUtf8());

	vl.append(sec);
	vl.append(usec);
	vl.append(file.toUtf8());
	vl.append(line);
	vl.append(func.toUtf8());

	ba = vl2ba(vl);
	return ba;
}

bool log_event::from_ba(QByteArray ba)
{
	valid = false;
	QVariantList vl = ba2vl(ba);
	if(vl.size() != 15) return false;

	if(vl[0].type() == QVariant::DateTime) current_time = vl[0].toDateTime();
	else return false;
	if(vl[1].type() == QVariant::ByteArray) client_name = QString(vl[1].toByteArray());
	else return false;
	if(vl[2].type() == QVariant::Int) msg_version = (quint8) vl[2].toInt();
	else return false;
	if(vl[3].type() == QVariant::Int) unicode = (quint8) vl[3].toInt();
	else return false;

	if(vl[4].type() == QVariant::ByteArray) server_name = QString(vl[4].toByteArray());
	else return false;
	if(vl[5].type() == QVariant::ByteArray) logger_name = QString(vl[5].toByteArray());
	else return false;
	if(vl[6].type() == QVariant::Int) log_level = vl[6].toInt();
	else return false;
	if(vl[7].type() == QVariant::ByteArray) NDC = QString(vl[7].toByteArray());
	else return false;
	if(vl[8].type() == QVariant::ByteArray) message = QString(vl[8].toByteArray());
	else return false;
	if(vl[9].type() == QVariant::ByteArray) thread = QString(vl[9].toByteArray());
	else return false;

	if(vl[10].type() == QVariant::Int) sec = vl[10].toInt();
	else return false;
	if(vl[11].type() == QVariant::Int) usec = vl[11].toInt();
	else return false;
	if(vl[12].type() == QVariant::ByteArray) file = QString(vl[12].toByteArray());
	else return false;
	if(vl[13].type() == QVariant::Int) line = vl[13].toInt();
	else return false;
	if(vl[14].type() == QVariant::ByteArray) func = QString(vl[14].toByteArray());
	else return false;

	valid = true;
	return valid;
}
